#pragma once

#include <vector>
#include <string>
#include <cmath>
#include "polynomial.h"

// 样条类型枚举，用于模板泛化
enum SplineType
{
    PiecewiseP,
    B,
    CardinalB,
};
// 边界类型枚举
enum BoundConditionType
{
    None, // 一阶样条插值无需边界条件
    Complete,
    SpecifiedSecondDerivatives,
    Natural,
    NotAKnot,
    Periodic,
    End, // 用于定理3.58中的边界条件
};

/**
 * @brief 模板样条类
 *
 * @tparam Dim 样条所拟合的曲线的维度
 * @tparam Order 样条的阶数
 * @tparam t 样条类型，包括PiecewiseP，B，CardinalB
 */
template <int Dim, int Order, SplineType t>
class Spline;

/**
 * @brief pp样条,实现线性样条和三阶样条三种边界条件
 *
 * @tparam Dim 维度
 * @tparam Order 阶数
 */
template <int Dim, int Order>
class Spline<Dim, Order, PiecewiseP>
{
public:
    // 样条拟合曲线矩阵，knots_curve接受Dim×N的曲线节点矩阵，每个列向量为曲线的节点坐标，bctp为边界条件类型，bc为Dim×(Order-1)的边界条件矩阵
    void fitCurve(const std::vector<std::vector<double>> &knots_curve, BoundConditionType bctp = None, const std::vector<std::vector<double>> &bc = std::vector<std::vector<double>>(0));

    template <int Dim_, int Order_, SplineType t_>
    friend double evaluate(const Spline<Dim_, Order_, t_> &sp, double x, int dim);

    template <int Dim_, int Order_, SplineType t_>
    friend void outputFile(const Spline<Dim_, Order_, t_> &sp, const std::string &filename, double grid);

private:
    std::vector<double> knots;                  // 存储参数节点
    std::vector<std::vector<Polynomial>> polys; // 存储Dim个分段多项式
};

/**
 * @brief B样条类边界条件为周期样条
 *
 * @tparam Dim 维度
 * @tparam Order 阶数,对任意阶数都能实现
 */
template <int Dim, int Order>
class Spline<Dim, Order, B>
{
public:
    void fitCurve(const std::vector<std::vector<double>> &knots_curve, BoundConditionType bctp = None, const std::vector<std::vector<double>> &bc = std::vector<std::vector<double>>(0));

    template <int Dim_, int Order_, SplineType t_>
    friend double evaluate(const Spline<Dim_, Order_, t_> &sp, double x, int dim);

    template <int Dim_, int Order_, SplineType t_>
    friend void outputFile(const Spline<Dim_, Order_, t_> &sp, const std::string &filename, double grid);

private:
    Polynomial setBasic(int n, int i, int pieceid) const; // 获取B样条基函数

    std::vector<std::vector<Polynomial>> polys; // 存储Dim个分段多项式
    std::vector<std::vector<Polynomial>> bbss;  // 存储N+Order-1个B样条基函数，每个基函数是限定在区间knots[0]到knots[N-1]上的分段多项式
    std::vector<double> knots;                  // 存储参数节点
};

/**
 * @brief 基数B样条,基于定理3.57和3.58,实现了quadratic样条,cubic样条
 *
 * @tparam Dim 维度
 * @tparam Order 阶数
 */
template <int Dim, int Order>
class Spline<Dim, Order, CardinalB>
{
public:
    void fitCurve(const std::vector<std::vector<double>> &knots_curve, BoundConditionType bctp, const std::vector<std::vector<double>> &bc = std::vector<std::vector<double>>(0));

    template <int Dim_, int Order_, SplineType t_>
    friend double evaluate(const Spline<Dim_, Order_, t_> &sp, double x, int dim);

    template <int Dim_, int Order_, SplineType t_>
    friend void outputFile(const Spline<Dim_, Order_, t_> &sp, const std::string &filename, double grid);

private:
    Polynomial setBasic(int n, int i, int pieceid) const; // 获取B样条基函数

    std::vector<std::vector<Polynomial>> polys; // 存储Dim个分段多项式
    std::vector<std::vector<Polynomial>> bbss;  // 存储N+Order-1个B样条基函数，每个基函数是限定在区间knots[0]到knots[N-1]上的分段多项式
    std::vector<double> knots;                  // 存储参数节点
};
